local require = require
local log    = require("modules.commhm.log")
local utils  = require("modules.commhm.utils")
local config = require("modules.business_common.business_config")
local http   = require("modules.business_common.utils.business_http")

local ngx = ngx
local type = type
local next = next
local pairs = pairs
local ipairs = ipairs
local tonumber = tonumber
local math = math
local math_abs = math.abs

local FOO = {
    _VERSION = "0.0.1",
    ACCOUNT_SEED = '522e6ccde68f4584a3282ce0b0eca1cf',

    -- 支付方式
    PAYMENTS = {
        MINIBEAN = 0, -- 迷你豆支付
        MINIDIAN = 1, -- 迷你点支付
        MINICOIN = 2, -- 迷你币支付
        STARCOIN = 3, -- 星星币支付
        FREECOIN = 4, -- 免费广告
        INVALID = 5
    },

    -- gmcost 已知的返回值
    RESULT = {
        OVERLOAD = 'OVERLOAD',  -- 过载
        SUCCESS  = 'SUCCESS',   -- 成功
    },

    OPTS = {
        RETRY = true    -- 重试
    }
}

-- 支付方式对应的物品id
FOO.PAYMENT_ITEM = {
    [FOO.PAYMENTS.MINIBEAN] = 10000,   -- 迷你豆
    [FOO.PAYMENTS.MINIDIAN] = 10009,   -- 迷你点
    [FOO.PAYMENTS.MINICOIN] = 10002,   -- 迷你币
}

local successful = function (response)
    return response and response.return_code == FOO.RESULT.SUCCESS
end


--不适用批量处理业务中,return_code为失败,但是在response.return_data的多业务处理中有成功的返回
local http_request = function(uin, request, retry)
    if not (request and request.cmd) then
        return nil
    end

    request.uin  = uin
    request.ts   = utils.now()
    request.sign = ngx.md5( utils.getStringBySortKey( request ) .. FOO.ACCOUNT_SEED )

    local server = config.get_cost_server_url(uin)
    local response = utils.json2table(http.Get(server, request) or '')

    -- 过载重试
    if retry and response and response.return_code == FOO.RESULT.OVERLOAD then
        response = utils.json2table(http.Get(server, request) or '')
    end

    -- 失败日志
    if not successful(response) then http.Log(utils.to_str({
        server   = server,
        request  = request,
        response = response
    })) end

    return response
end

-- !@  获取账号服物品信息
FOO.items_query_all = function (uin)
    local result = http_request(uin, {
        cmd    = "query",
        path   = "Account.ItemInfo",
    })
    if not successful(result) then return false end

    return true, (result.return_result or {})['Account.ItemInfo']
end

-- !@ 获取拥有物品个数
FOO.item_query = function (uin, itemid)
    local result, info = FOO.items_query_all(uin)
    if not result then
        return false
    end

    for _, item in ipairs(info or {}) do
        if item.ItemID == itemid then
            return true, item.Num
        end
    end

    return true, 0
end

--! @brief 扣除物品
FOO.item_remove = function (uin, itemid, num, reason, ext)
    itemid = tonumber(itemid) or 0
    num    = tonumber(num)    or 0
    
    if itemid == 0 or num == 0 then
        log.day_list("business_account", "item_remove", "id or num is nil", uin, reason, utils.to_str({id=itemid, num=num}))
        return false
    end

    local result = http_request(uin, {
        cmd    = "item_rm",
        id     = itemid,
        num    = math_abs(num),
        reason = reason,
        ext    = type(ext) == "table" and utils.table2json(ext) or ext,
    }, FOO.OPTS.RETRY)
    return successful(result) and true or false
end

--! @brief 根据物品列表删除物品
--! @param items: {{id=xx,num=xx},{id=xx,num=xx},...}
--! @param exts : {{item_id=xx,item_num=xx,item_type=xx,item_price=xx},...}
FOO.items_remove = function (uin, items, reason, ext)
    if not uin then
        return false
    end

    local items_ = {}
    for _, item in pairs(items or {}) do
        local id  = tonumber(item.id)  or 0
        local num = tonumber(item.num) or 0

        if id > 0 and num > 0 then
            items_[#items_+1] = {id=id, num=num}
        else
            log.day_list('business_account', "item_list_remove", "id or num is nil", uin, reason, utils.to_str(items))
        end
    end

    if not next(items_) then
        log.day_list('business_account', "item_list_remove", "items is nil", uin, reason, utils.to_str(items))
        return false
    end

    local result = http_request(uin, {
        cmd      ='items_rm',
        items    = utils.table2json(items_),
        reason   = reason,
        ext      = type(ext) == "table" and utils.table2json(ext) or ext,
    }, FOO.OPTS.RETRY)

    return successful(result) and true or false
end

--! @brief 新增物品
--! @param items: {{id=xx,num=xx},{id=xx,num=xx},...}
--! @param exts : {{item_id=xx,item_num=xx,item_type=xx,item_price=xx},...}
FOO.items_add = function (uin, items, reason, ext)
    if not uin then
        return false
    end

    local items_ = {}
    for _, item in pairs(items or {}) do
        local id  = tonumber(item.id)  or 0
        local num = tonumber(item.num) or 0

        if id > 0 and num > 0 then
            items_[#items_+1] = {id=id, num=num}
        else
            log.day_list('business_account', "items_add", "id or num is nil", uin, reason, utils.to_str(items))
        end
    end

    if not next(items_) then
        log.day_list('business_account', "items_add", "items is nil", uin, reason, utils.to_str(items))
        return false
    end

    return http_request(uin, {
        cmd      ='items_add',
        items    = utils.table2json(items_),
        reason   = reason,
        ext      = type(ext) == "table" and utils.table2json(ext) or ext,
    }, FOO.OPTS.RETRY)
end

-- !@ 迷你币
FOO.minicoin_remove = function (uin, num, reason, ext)
    num = tonumber(num) or 0
    if not uin or num == 0 then
        return false
    end

    local result = http_request(uin, {
        cmd      = "minicoin_rm",       --减迷你币
        minicoin = math_abs(num),
        reason   = reason,
        ext      = type(ext) == "table" and utils.table2json(ext) or ext,
    }, FOO.OPTS.RETRY)

    return successful(result) and true or false
end

-- !@ 迷你点
FOO.minidian_remove = function (uin, num, reason, ext)
    num = tonumber(num) or 0
    if not uin or num == 0 then
        return false
    end

    local result = http_request(uin, {
        cmd      = "adpoint_rm",       --减迷你点
        num      = math_abs(num),
        reason   = reason,
        ext      = type(ext) == "table" and utils.table2json(ext) or ext,
    }, FOO.OPTS.RETRY)

    return successful(result) and true or false
end

-- !@ 迷你豆
FOO.minibean_remove = function (uin, num, reason, ext)
    num = tonumber(num) or 0
    if not uin or num == 0 then
        return false
    end

    local result = http_request(uin, {
        cmd      = "minibean_rm",       --减迷你豆
        minibean = math_abs(num),
        reason   = reason,
        ext      = type(ext) == "table" and utils.table2json(ext) or ext,
    }, FOO.OPTS.RETRY)

    return successful(result) and true or false
end

-- !@ 家园-植物经验
FOO.chestexp_add = function (uin, exp, reason)
    exp = tonumber(exp) or 0
    if not uin or exp == 0 then
        return false
    end

    local result = http_request(uin, {
        cmd      = "chestexp_add",
        exp      = math_abs(exp),
        reason   = reason,
    })

    return successful(result) and true or false
end

local payfunc = {
    [FOO.PAYMENTS.MINIBEAN] = FOO.minibean_remove,
    [FOO.PAYMENTS.MINIDIAN] = FOO.minidian_remove,
    [FOO.PAYMENTS.MINICOIN] = FOO.minicoin_remove,
    [FOO.PAYMENTS.STARCOIN] = function(...) return true end,
    [FOO.PAYMENTS.FREECOIN] = function(...) return true end
}

-- !@ 货币支付
FOO.defray = function(uin, price_type, price_num, reason, ext)
    return payfunc[price_type] and payfunc[price_type](uin, price_num, reason, ext) or false
end

-- !@ 货币返还
FOO.refund = function (uin, price_type, price_num, reason)
    local result = true
    local itemid = FOO.PAYMENT_ITEM[price_type]
    if itemid then

        -- 按配置返回道具
        local item_refund = {{id = itemid, num = price_num}}
        result = FOO.items_add(uin, item_refund, reason)
    end

    log.day_list("business_account", "refund_item", utils.to_str({
        uin    = uin,
        type   = price_type,
        num    = price_num,
        reason = reason,
        result = result
    }))

    return result
end

--! @ 查询 已拥有的永久皮肤
--! @ 返回： {id，id1,...}
-- 历史原因导致皮肤数据存存储在两个仓库，需要同时查询后返回最终结果
FOO.query_owned_skin_account = function(uin)
    uin = uin or ngx.ctx.m_params.uin
    local repositories = {
        ['Account.BillDataSvr']  = function (data)
            return (data['Account.BillDataSvr'] or {}).RoleSkinInfo or {}
        end,
        ['leveldb.RoleSkinInfo'] = function (data)
            return data['leveldb.RoleSkinInfo'] or {}
        end
    }

    local skin_list = {}
    for query_path, get_skin_info in pairs(repositories or {}) do
        local result = http_request(uin, {
            cmd      = "query",
            path     = query_path,
            ignore   = 1,
        })
        if not successful(result) then
            return false
        end
        for _, roleskin in pairs(get_skin_info(result.return_result or {})) do
            if type(roleskin) == "table" and tonumber(roleskin.ExpireTime) == -1 then --永久
                skin_list[#skin_list+1] = roleskin.SkinID
            end
        end
    end

	return true, skin_list
end

--! @ 查询 单个皮肤到期时间
--! @ 返回： 到期时间，永久为 -1 没有皮肤返回 0， 有到期时间，则返回到期时间
-- 历史原因导致皮肤数据存存储在两个仓库，需要同时查询后返回最终结果
FOO.query_skin_expiretime = function(skin_id, uin)
    uin = uin or ngx.ctx.m_params.uin
    local repositories = {
        ['Account.BillDataSvr']  = function (data)
            return (data['Account.BillDataSvr'] or {}).RoleSkinInfo or {}
        end,
        ['leveldb.RoleSkinInfo'] = function (data)
            return data['leveldb.RoleSkinInfo'] or {}
        end
    }

    for query_path, get_skin_info in pairs(repositories or {}) do
        local result = http_request(uin, {
            cmd      = "query",
            path     = query_path,
            ignore   = 1,
        })
        if not successful(result) then
            return false
        end
        for _, roleskin in pairs(get_skin_info(result.return_result or {})) do
            if tonumber(skin_id) == tonumber(roleskin.SkinID) then
                return true, tonumber(roleskin.ExpireTime)
            end
        end
    end

	return true, 0
end

local skinid_add_list = function (skinlist)
    local unique_list, final_list = {}, {}
    for _, skinid in pairs(skinlist or {}) do
        unique_list[skinid] = 1
    end
    for skinid, _ in pairs(unique_list) do
        final_list[#final_list + 1] = { SkinID = skinid }
    end
    return final_list
end

-- skinlist = {skin1, skin2, skin3...}
-- desc : 当前仅用于发送永久皮肤。
FOO.add_skin_list = function (skinlist, reason, uin)
    uin = uin or ngx.ctx.m_params.uin
    skinlist = skinid_add_list(skinlist)
    if utils.tableIsEmpty(skinlist) then
        return false, "nothing to add"
    end
    local result = http_request(uin, {
        cmd      ='skins_add',
        items    = utils.table2json(skinlist),
        reason   = reason,
    }, FOO.OPTS.RETRY)

    if not successful(result) then
        return false, result
    end

    return true
end

-- ! @ ExpireTimeType : 1, 7天， 2，30天， 4，1天  不在124，都是永久
FOO.add_skin = function (skinid, reason, uin, ExpireTimeType)
    uin = uin or ngx.ctx.m_params.uin
    local result = http_request(uin, {
        cmd      = "skin_add",
        reason   = reason,
        SkinID   = skinid,
        ExpireTimeType = ExpireTimeType or -1,
    })

    return successful(result) and true or false
end


FOO.remove_skin = function (skinid, reason, uin)
    uin = uin or ngx.ctx.m_params.uin
    reason = reason or "business_cmd"
    local result = http_request(uin,  {
        cmd = 'skin_rm',
        SkinID = skinid,
        reason = reason,
    })

    return successful(result) and true or false
end

FOO.use_skin  = function (skinid, uin)
    uin = uin or ngx.ctx.m_params.uin
    local result = http_request(uin,  {
        cmd = 'skin_use',
        SkinID = skinid,
    })

    return successful(result) and true or false
end

FOO.modify_skins_expiretime = function(uin, skinlst,  reason)
    local str_lst = utils.table2json(skinlst)
    local  info = {cmd = 'skin_modify_expiretime_batch',  items  = str_lst, reason = reason, uin = uin }
    local result = http_request(uin,  info)

    return successful(result) and true or false
end

-- !@会员状态变化
FOO.vip_changed = function ( uin, vip_open )
    uin = uin or ngx.ctx.m_params.uin
    local result = http_request(uin,  {
        cmd = 'vip_changed',
        vip_open = vip_open,
    })

    return successful(result) and true or false
end


--! @ 查询 已拥有的永久坐骑
--! @ 返回： {id，id1,...}
FOO.query_owned_mount_account = function(uin)
    uin = uin or ngx.ctx.m_params.uin
    local result = http_request(uin, {
        cmd      = "query_rider_all",
        ignore   = 1,
    })
    if not successful(result) then
        return false, {}
    end

    local mount_list = {}
    for _, info in pairs(result.return_result or {}) do
        for i=0, info.RiderLevel or 0 do
            mount_list[#mount_list + 1] = info.RiderID + i
        end
    end

    return true, mount_list
end


FOO.add_mount = function (mountid, level, reason, uin)
    uin = uin or ngx.ctx.m_params.uin
    local result = http_request(uin, {
        cmd        = "rider_upgrade",   -- rider_add 接口只能加1级坐骑
        reason     = reason,
        RiderID    = mountid,
        RiderLevel = level,
    })

    return successful(result) and true or false
end


FOO.add_avatar = function (modelid, reason, dst_uin, src_uin)
    dst_uin = dst_uin or ngx.ctx.m_params.uin
    src_uin = src_uin or dst_uin
    local result = http_request(dst_uin, {
        cmd            = "avatar_skin_add",
        reason         = reason,
        modelid        = modelid,
        author_uin     = 1000,
        ExpireTimeType = -1,
    })

    return successful(result) and true or false
end


local avatar_request = function (uin, request)
    uin = uin or ngx.ctx.m_params.uin

    request.uin = uin
    local server = config.get_avatar_server_url(uin)
    local response = utils.json2table(http.Get(server, request) or 'nil')

    if response and tonumber(response.code) == 0 then
        return true, response.msg
    end

    http.Log(utils.to_str({ server = server, request = request, response = response }))
    return false
end


-- 查询是否有永久avatar
--! @ 返回： {id，id1,...}
FOO.query_owned_avatar_account = function(uin)
    local ret, result = avatar_request(uin, {
        reqfield = 'ModelID,ExpireTime'
    })
    if not ret then
        return false, {}
    end

    local avatar_list = {}
    for _, info in ipairs(result or {}) do
        if info.ExpireTime == -1 then
            avatar_list[#avatar_list + 1] = info.ModelID
        end
    end
    return true, avatar_list
end

return FOO
